﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TCG.Engine.Impacts;
using TCG.Engine.Libraries;
using TCG.Engine.Effects;
using TCG.Engine.WiningConditions;

namespace TCG.Engine.Core
{
    public class Match
    {
        public Player Player1 { get; private set; }
        public Player Player2 { get; private set; }

        public int Round { get; private set; }

        public Player ActivePlayer { get; private set; }
        public Player EnemyPlayer
        {
            get
            {
                return ActivePlayer == Player1 ? Player2 : Player1;
            }
        }

        MatchOptions options;

        MatchResult matchResult = new MatchResult();
        public MatchResult MatchResult
        {
            get
            {
                return matchResult;
            }
            set
            {
                matchResult = value;
            }
        }

        public void Start(MatchOptions options)
        {
            this.options = options;

            Player1 = new Player(options.Player1);
            Player2 = new Player(options.Player2);
            ActivePlayer = Player1;

            var decks = Env.Get<IDeckLibrary>();

            Player1.Deck = decks.CreateDeck(options.Player1.DeckId);
            Player2.Deck = decks.CreateDeck(options.Player2.DeckId);
        }

        public void NextRound()
        {
            if (Round == 0)
            {
                Enumerable.Range(0, options.StartCardsCount).ToList().ForEach(i => Player1.Draw());
                Enumerable.Range(0, options.StartCardsCount).ToList().ForEach(i => Player2.Draw());
            }
            Round++;
        }

        public void StartTurn()
        {
            ProcessEffects(ActivePlayer, EnemyPlayer, e => e.StartTurn(this));

            DrawCard();

            GenerateResources();
        }

        private void GenerateResources()
        {
            if (Round != 1)
            {
                ActivePlayer.Gold += ActivePlayer.GoldIncome;
                ActivePlayer.Mana += ActivePlayer.ManaIncome;
            }
        }

        private void DrawCard()
        {
            if (!(Round == 1 && ActivePlayer == Player1))
            {
                ActivePlayer.Draw();
            }
        }

        public bool PlayerMove(string cardId)
        {
            if (!ActivePlayer.Hand.Any())
                return false;

            var c = ActivePlayer.Move(cardId);
            if (c == null)
                return false;

            var mc = CreateMoveContext(c);

            ProcessEffects(mc.ActivePlayer, mc.EnemyPlayer, e => e.StartMove(mc));

            c.Apply(mc);

            ProcessEffects(mc.ActivePlayer, mc.EnemyPlayer, e => e.Move(mc));

            ApplyImpacts(mc);

            mc.ActivePlayer.Hand.Remove(c);
            mc.ActivePlayer.Deck.Sideboard.Add(c);

            ProcessEffects(mc.ActivePlayer, mc.EnemyPlayer, e => e.EndMove(mc));

            if (ValidateMoveContext(mc))
            {
                if (options.WinningCondition != null)
                    matchResult = options.WinningCondition.Validate(mc);
                ApplyMoveContext(mc);
            }
            else
            {
                ActivePlayer.Interaction.NotEnoughResources();
            }
            return true;
        }

        private bool ValidateMoveContext(MoveContext mc)
        {
            return ValidatePlayerResources(mc.ActivePlayer);
        }

        private bool ValidatePlayerResources(Player player)
        {
            return player.Gold >= 0 && player.Mana >= 0;
        }

        private void ProcessEffects(Player active, Player enemy, Action<IEffect> action)
        {
            active.Effects.ToList().ForEach(action);
            enemy.Effects.ToList().ForEach(action);
        }

        public void EndTurn()
        {
            ProcessEffects(ActivePlayer, EnemyPlayer, e => e.EndTurn(this));

            DiscardCards();

            ActivePlayer = EnemyPlayer;
            if (ActivePlayer == Player1)
                NextRound();
        }

        private void DiscardCards()
        {
            if (ActivePlayer.Hand.Count > options.MaxCardsInHand)
            {
                var cards = ActivePlayer.Interaction.DiscardCards(ActivePlayer.Hand.Count - options.MaxCardsInHand);

                foreach (var c in cards)
                {
                    ActivePlayer.Hand.Remove(c);
                    ActivePlayer.Deck.Sideboard.Add(c);
                }
            }
        }

        private void ApplyMoveContext(MoveContext mc)
        {
            Player.Copy(this.Player1, mc.Player1);
            Player.Copy(this.Player2, mc.Player2);
        }

        private MoveContext CreateMoveContext(Card c)
        {
            var result = new MoveContext();

            result.Player1 = Player1.Clone();
            result.Player2 = Player2.Clone();

            if (ActivePlayer == Player1)
            {
                result.ActivePlayer = result.Player1;
                result.EnemyPlayer = result.Player2;
            }
            else
            {
                result.ActivePlayer = result.Player2;
                result.EnemyPlayer = result.Player1;
            }
            result.Card = c;

            return result;
        }

        private void ApplyImpacts(MoveContext mc)
        {
            foreach (var i in mc.Impacts)
            {
                i.Apply(mc);
            }
        }
    }
}
